package py.edu.ucom.poo.waterloo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import java.util.Random;
import java.util.Set;

import java.util.logging.Level;
import java.util.logging.Logger;
import py.edu.ucom.poo.waterloo.agente.Agente;
import py.edu.ucom.poo.waterloo.gui.WaterlooListener;
import py.edu.ucom.poo.waterloo.log.logger;

/**
 * @author WATERLOO TEAM
 *Esta clase es donde esta la logica de todo el juego y es el encargado de que 
 *los agentes validen sus tableros y que muevan sus piezas de forma correcta
 */
public class SandBox implements SandBoxInterface{
	
	/**
	 * Constante para el color Rojo
	 */
	public static int COLOR_RED=1;
	/**
	 * Constante para el color Azul
	 */
	public static int COLOR_BLUE=2;
	/**
	 * Constante para indicar que la casilla del tablero de juego esta vacia
	 */
	public static int CASILLA_VACIA=-1;
	/**
	 * Constante que indica el valor de la pieza espia
	 */
	public static int ESPIA=0;
	/**
	 * Constante que indica el valor de la pieza bandera
	 */
	public static int BANDERA=1;
	/**
	 * Constante que indica el valor de la pieza soldado
	 */
	public static int SOLDADO=2;
	/**
	 * Constante que indica el valor de la pieza cabo desarmador
	 */
	public static int CABO_DESARMADOR=3;
	/**
	 * Constante que indica el valor de la pieza sargento
	 */
	public static int SARGENTO=4;
	/**
	 * Constante que indica el valor de la pieza capitan
	 */
	public static int CAPITAN=5;
	/**
	 * Constante que indica el valor de la pieza mayor
	 */
	public static int MAYOR=6;
	/**
	 * Constante que indica el valor de la pieza teniente
	 */
	public static int TENIENTE=7;
	/**
	 * Constante que indica el valor de la pieza coronel
	 */
	public static int CORONEL=8;
	/**
	 * Constante que indica el valor de la pieza general
	 */
	public static int GENERAL=9;
	/**
	 * Constante que indica el valor de la pieza mariscal
	 */
	public static int MARISCAL=10;
	/**
	 * Constante que indica el valor de la pieza bomba
	 */
	public static int BOMBA=11;

	/**
	 * Constante que indica que en la configuracion del juego se eligio Sistema Americano
	 */
	public static int SISTEMA_AMERICANO=1;
	/**
	 * Constante que indica que en la configuracion del juego se eligio Sistema Europeo
	 */
	public static int SISTEMA_EUROPEO=2;
	/**
	 * Constante que indica la opcion de quitar las bombas cuando estas son detonadas
	 */
	public static int OPCION_QUITAR_BOMBA=1;
	/**
	 * Constante que indica la opcion de no quitar las bombas cuando estas son detonadas
	 */
	public static int OPCION_NO_QUITAR_BOMBA=2;
	/**
	 * Propiedad de la clase que identifica todas las fichas de color rojo
	 */
	private Ficha[] fichaRojo;
	/**
	 * Propiedad de la clase que identifica todas las fichas de color azul
	 */
	private Ficha[] fichaAzul;
	
	/**
	 * Propiedad de la clase que indica el color del agente 1 que jugara el juego
	 */
	int colorAgente1;
	/**
	 * Propiedad de la clase que indica el color del agente 2 que jugara el juego
	 */
	int colorAgente2;
	/**
	 * Propiedad de la clase donde se guarda luego del sorteo de colores al agente de color rojo
	 */
	private Agente jugador1;
	
	/**
	 * Propiedad de la clase donde se guarda luego del sorteo de colores al agente de color azul
	 */
	private Agente jugador2;
	/**
	 * Propiedad de la clase que representa al tablero de juego
	 */
	private Tablero tableroJuego;
	/**
	 * Propiedad de la clase donde se guarda el sistema de juego que ha sido elegido durante la configuracion del juego
	 */
	private int sistemaJuego;
	
	/**
	 * Propiedad de la clase donde se guarda la opcion de quitar o no las bombas una vez que estas detonan
	 */
	private int opcionBomba;
	/**
	 * Propiedad de la clase que representa qe agente esta jugando actualmente
	 */
	private Agente agenteJugando;

	/**
	 * Propiedad de la clase que representa el tiempo maximo permitido por jugada configurado en la pantalla inicial del juego  
	 */
	private int opcionTiempo;
	
	/**
	 * Propiedad de la clase que indica si hubo o no ganador del juego
	 */
	private boolean finJuego;
	
	/**
	 * Array donde se colocan los listeners que escuchan los eventos del sandbox
	 */
	private Collection<WaterlooListener> waterlooListeners;
	/**
	 * Propiedad de la clase que representa el archivo de configuracion donde se encuentran los nombres de los agentes habilitados para
	 * el juego con sus respectivos classpath
	 */
	private Properties agentesProperties;
	/**
	 * Propiedad de la clase que representa el nombre del agente que es el jugador 1
	 */
	
	private String nombreJugador1;
	/**
	 * Propiedad de la clase que representa el nombre del agente que es el jugador 2
	 */
	private String nombreJugador2;
	
	/**
	 * Propiedad de la clase que indica si se debe esperar por el GUI o no
	 */
	private boolean waitingForGUI;
    
	
	/**
	 * Constructor del sandBox crea un objeto sandBox, instancia un objeto properties que sera el archivo de
	 * configuracion donde se encuentran los agentes registrados para jugar en el juego
	 * @throws WaterlooApplicationException lanza esta excepcion si ocurre alguno de los siguientes errores
	 * 1- Si no se encuentra el archivo properties que es el archivo donde se encuentran los agentes
	 * 2- Si ocurre algun problema al leer el archivo de configuracion de agentes
	 */
	
	@SuppressWarnings("unchecked")
	public SandBox() throws WaterlooApplicationException{
		//Instancia el objeto properties que sera el archivo de configuracion que contiene los agentes registrados para
		//el juego
		Properties prop=new Properties();
		//Guarda el nombre del archivo de configuracion 
		String fileName="agentes.properties";
		try {
			//Lee el archivo de configuracion de nombre fileName
			prop.load(new FileReader(fileName));
			Iterator it =prop.keySet().iterator();
			while(it.hasNext()){
				String clave=(String) it.next();
				String valor=(String) prop.get(clave);
				System.out.println(clave+" -- "+valor);
			}
			
			//Llama al metodo iniciar pasandole como parametro el archivo de configuracion
			iniciar( prop);
			//Excepciones lanzadas si ocurren algunos de los errores abajo se�alados
		} catch (FileNotFoundException e) {
			throw new WaterlooApplicationException("No se encontro el archivo ",e);
		} catch (IOException e) {
			throw new WaterlooApplicationException("Problema al leer el archivo ",e);
		}
	}

	/**
	 * Registra todos los listeners en este caso seria solamente el GUI y el logguer
	 * @param l este parametro indica el waterloolistener que se registrara
	 */
	public void addWaterLooListener(WaterlooListener l){
		boolean estado=waterlooListeners.add(l);
		if(estado){
			System.out.println("agrego con exito.......................................................");
		}
		System.out.println(waterlooListeners.size());
		
	}
	
	/**
	 * Lee el archivo de configuracion desde el cual seran extraidos los agentes que jugaran en el juego,
	 * inicializa el tablero de juego y crea y setea las fichas para cada uno de los jugadores
	 * @param prop es el archivo de configuracion que contiene los agentes disponibles
	 *
	 */
	private void iniciar( Properties prop) {
		this.agentesProperties=prop;
		this.finJuego=false;
		tableroJuego=new Tablero();//Crea el tablero de juego como una matriz de Casillas
		tableroJuego.inicializarCasillaTablero();
		fichaRojo=new Ficha[40];//Crea el conjunto de fichas para el jugador de rojo
		fichaAzul=new Ficha[40];//Crea el conjunto de fichas para el jugador de azul
		this.crearFichaParaAgente();//Crea las fichas para ambos jugadores
		this.setearFicha(fichaRojo);//Setea las fichas del color rojo
		this.setearFicha(fichaAzul);//Setea las fichas del color azul
		this.waterlooListeners=new ArrayList<WaterlooListener>();
	}
	
	
	Agente getJugador1() {
		return jugador1;
	}
	void setJugador1(Agente jugador1) {
		this.jugador1 = jugador1;
	}
	Agente getJugador2() {
		return jugador2;
	}
	void setJugador2(Agente jugador2) {
		this.jugador2 = jugador2;
	}
	/**
	 * Este metodo crea las fichas para el jugador de color rojo y azul y guarda
	 * en el array de fichas del color correspondiente
	 */
	private void crearFichaParaAgente(){
		for(int i=0;i<40;i++){
			Ficha ficha=new Ficha();
			ficha.setColor(COLOR_RED);
			this.fichaRojo[i]=ficha;
			Ficha ficha1=new Ficha();
			ficha1.setColor(COLOR_BLUE);
			this.fichaAzul[i]=ficha1;
		}
	}
	/**
	 * Este metodo lo que hace es colocar las fichas segun la cantidad y seteando
	 * Rango y Valor 
	 * @param ficha recibe el array de fichas de un color determinado
	 */
	private void setearFicha(Ficha [] ficha){
		int [] cantidadPieza;
		int k=0;
		cantidadPieza=new int[12];
		cantidadPieza[0]=1;//Cantidad de Espia
		cantidadPieza[1]=1;//Cantidad de Bandera
		cantidadPieza[2]=8;//Cantidad de Soldados
		cantidadPieza[3]=5;//Cantidad de Cabos desarmadores
		cantidadPieza[4]=4;//Cantidad de Sargentos
		cantidadPieza[5]=4;//Cantidad de Capitanes
		cantidadPieza[6]=4;//Cantidad de Mayores
		cantidadPieza[7]=3;//Cantidad de Tenientes
		cantidadPieza[8]=2;//Cantidad de Coroneles
		cantidadPieza[9]=1;//Cantidad de General
		cantidadPieza[10]=1;//Cantidad de Mariscal
		cantidadPieza[11]=6;//Cantidad de Bombas
		for(int i=0;i<12;i++){
			for(int j=0;j<cantidadPieza[i];j++,k++){
				ficha[k].setValor(i);
				switch(ficha[k].getValor()){
				case 0:ficha[k].setRango("Espia");break;
				case 1:ficha[k].setRango("Bandera");break;
				case 2:ficha[k].setRango("Soldado");break;
				case 3:ficha[k].setRango("Cabo desarmador");break;
				case 4:ficha[k].setRango("Sargento");break;
				case 5:ficha[k].setRango("Capitan");break;
				case 6:ficha[k].setRango("Mayor");break;
				case 7:ficha[k].setRango("Teniente");break;
				case 8:ficha[k].setRango("Coronel");break;
				case 9:ficha[k].setRango("General");break;
				case 10:ficha[k].setRango("Mariscal");break;
				case 11:ficha[k].setRango("Bomba");break;
				}
			}
		}
	}
	/**
	 * Sortea entre los jugadores quien sera el color rojo y los coloca dentro
	 * del sandbox
	 * 
	 * */
	@SuppressWarnings("static-access")
	private void sorteoFicha(Agente a1,Agente a2){
		Random r=new Random();
		if(r.nextInt()%2==0){
			this.setJugador1(a1);
			this.setJugador2(a2);
			
		}
		else{
			this.setJugador1(a2);
			this.setJugador2(a1);
		}
		jugador1.setColor(this.COLOR_RED);
		jugador2.setColor(this.COLOR_BLUE);
	}
	/**
	 * Valida los tableros de los agentes 
	 * @param t es el tablero del agente que se quiere validar
	 * @param a es el agente del cual se quiere validar el tablero
	 * @return true si el tablero es valido y false en otro caso
	 */
	private boolean validarTableroPorAgente(Tablero t,Agente a){
		if(a.getFicha()[0].getColor()==COLOR_RED){
			for(int i=0;i<10;i++){
				for(int j=0;j<10;j++){
					if(t.getCasillaDelTablero(i, j).getFicha().getRango()!=null){
						if(t.getCasillaDelTablero(i, j).getPosX()<4){
							this.tableroJuego.setCasillaEnTablero(i, j, a.getMapaInterno().getCasillaDelTablero(i, j));
						}
					}
				}
			}
		}
		else{
			for(int i=0;i<10;i++){
				for(int j=0;j<10;j++){
					if(t.getCasillaDelTablero(i, j).getFicha().getRango()!=null){
						if(t.getCasillaDelTablero(i, j).getPosX()>5){
							this.tableroJuego.setCasillaEnTablero(i, j, a.getMapaInterno().getCasillaDelTablero(i, j));
						}
					}
				}
			}
		}
		
		return true;
	}
	/**
	 * Obtiene del archivo de configuracion la lista de agentes
	 * @return la lista de agentes disponibles
	 */
	@SuppressWarnings("unchecked")
	public Set getListaDeAgentes(){
		return agentesProperties.keySet();
	}
	
	/**
	 * Este metodo se encarga de instanciar las clases de los agentes elegidos para el juego
	 * @param agentName Recibe el nombre del agente 
	 * @return retorna el objeto agente de la clase
	 * @throws WaterlooApplicationException lanza esta excepcion si al tratar de instanciar la clase de agente 
	 * ocurre algun error
	 */
	@SuppressWarnings("unchecked")
	private Agente getAgenteFromName(String agentName) throws WaterlooApplicationException{
		//Verifica que exista dentro del archivo de configuracion el agente de agentName
		String agentClassName=(String) agentesProperties.get(agentName);
		//Lanza una excepcion si el agentName no existe
		if(agentClassName==null){    
			throw new WaterlooApplicationException("No se encontro la configuracion para el agentName "+agentName);
		}
		try {
			//Registra como clase el agentName del archivo de configuracion
			Class agentClass=Class.forName(agentClassName);
			//Instancia el agente de la clase obtenida en agentClass
			Agente agente=(Agente) agentClass.newInstance();
			//Retorna el agente instanciado
			return agente;
			//Lanza alguna de las excepciones si ocurre algunos de los errores 
		} catch (ClassNotFoundException e) {
			throw new WaterlooApplicationException("No se encontro la clase "+agentClassName+" para el agentName = "+agentName,e);
		} catch (InstantiationException e) {
			throw new WaterlooApplicationException("No se pudo instanciar la clase "+agentClassName,e);
		} catch (IllegalAccessException e) {
			throw new WaterlooApplicationException("No se pudo instanciar la clase "+agentClassName,e);
		}catch(ClassCastException e){
			throw new WaterlooApplicationException("El agente "+agentClassName+" debe heredar de la clase Agente",e);
		}
	}
	
		/**
		 * Metodo que inicia el juego 
		 * @param inicioJuegoInfo Parametro recibido del GUI que contiene la configuracion del juego
		 * @throws WaterlooApplicationException excepcion lanzada si existe error a nivel de aplicacion
		 * @throws WaterlooLogicException excepcion lanzada si existe error a nivel de logica del juego
		 */
		public void iniciarJuego(InicioJuegoInfo inicioJuegoInfo) throws WaterlooApplicationException, WaterlooLogicException {
			//Copia los agentes elegidos del GUI	
			Agente a1=getAgenteFromName(inicioJuegoInfo.getAgente1());
			Agente a2=getAgenteFromName(inicioJuegoInfo.getAgente2());
			//Registra los agentes a la interfaz con el sandBox
			a1.setSandBoxInterface(this);
			a2.setSandBoxInterface(this);
			//Setea sistema del juego (Europeo o Americano)
			this.sistemaJuego=inicioJuegoInfo.getSistemaJuego();
			//Setea la opcion de quitar o no quitar las bombas una vez que son activadas
			this.opcionBomba=inicioJuegoInfo.getOpcionBomba();
			//Setea la opcion de duracion en segundos de cada jugada
			this.opcionTiempo=inicioJuegoInfo.getOpcionTiempo();
			//Sortea entre los agentes elegidos quien jugara primero y sera del color rojo
			this.sorteoFicha(a1,a2);
			//Pasa fichas de su color al jugador y recibe el tablero con las fichas colocadas
			Tablero tabrojo=this.jugador1.ubicarFichas(this.fichaRojo, this.tableroJuego);
			//Valida que el tablero recibido sea valido
			boolean isvalido=this.validarTableroPorAgente(tabrojo,this.getJugador1());
			if(!isvalido){
				throw new WaterlooApplicationException("El tablero es invalido");
			}
			Tablero tabazul=this.jugador2.ubicarFichas(this.fichaAzul, this.tableroJuego);
			boolean isvalido1=this.validarTableroPorAgente(tabazul, this.getJugador2());
			if(!isvalido1){
				throw new WaterlooApplicationException("El tablero es invalido");
			}
			//Avisa al GUI que debe pintar el tablero de acuerdo al tablero logico que se obtiene al combinar
			//ambos tableros validos de los jugadores
			this.fireIniciarTableroGui(this.tableroJuego);
			@SuppressWarnings("unused")
			int i=0;
			this.agenteJugando=this.jugador1;
			
			//Comienza el juego el jugador 1 que sera el color rojo
			while(!this.finJuego){
                            try {
                                long inicio;
                                long fin = 0;
                                
                                Movimiento mov;
                                inicio = System.currentTimeMillis();
                                mov = agenteJugando.jugar();
                                System.out.println("SandBox.while. Agente" + agenteJugando.getColor() + " :" + mov.getOrigen().getPosX() + ", " + mov.getOrigen().getPosY() + " === " + mov.getDestino().getPosX() + " , " + mov.getDestino().getPosY());
                                //Termina el tiempo para el turno actual
                                fin = System.currentTimeMillis();
                                //Verifica si el jugador excedio el tiempo permitido por jugada
                                if (this.pierdeTurno(inicio, fin, agenteJugando)) {
                                    agenteJugando = this.cambiarAgente(agenteJugando);
                                    this.fireFinTiempoTurno(agenteJugando);
                                    continue;
                                }
                                //Si no se excedio el tiempo permitido por jugada se valida el movimiento efectuado
                                this.validarMovimiento(this.tableroJuego, mov, this.agenteJugando);
                                while (waitingForGUI) {
                                    try {
                                        Thread.sleep(200);
                    } catch (InterruptedException e) {

                                        e.printStackTrace();
                                    }
                                }
                                //imprimirTablero(tableroJuego);
                                System.out.println("SandBox.while: cambiando turno");
                                agenteJugando = cambiarAgente(agenteJugando);
                                this.fireCambioTurno(agenteJugando);
                            //Prueba
            } catch (Exception ex) {
                                Logger.getLogger(SandBox.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        //Prueba 
                        }
		}
		public Agente cambiarAgente(Agente agente){
			if(agente.equals(this.jugador1)==true)
				return this.jugador2;
			return jugador1;
		}
		public boolean pierdeTurno(long inicio,long fin,Agente a){
			if(((fin/1000)-(inicio/1000))>this.opcionTiempo){
				this.fireCambioTurno(this.cambiarAgente(a));
				return true;
			}
			return false;
		}
		
	/**
	 * Metodo de la interfaz con el GUI que avisa cuando el agente tardo mas tiempo
	 * que el elegido en la configuracion del juego
	 * @param agenteQueObtieneTurno Agente que tendra el turno en ese instante
	 */
	@SuppressWarnings("unused")
	private void fireFinTiempoTurno(Agente agenteQueObtieneTurno){
		Iterator<WaterlooListener> it=waterlooListeners.iterator();
		while(it.hasNext()){
			WaterlooListener w=it.next();
			w.cambioTurno(agenteQueObtieneTurno);
			
		}
	}
	/**
	 * Metodo de la interfaz con el GUI que avisa cuando el agente termino de realizar 
	 * la jugada
	 * @param agenteQueObtieneTurno Agente que obtendra el turno en ese instante
	 */
	private void fireCambioTurno(Agente agenteQueObtieneTurno){
		System.out.println("SandBox.fireCambioTurno: cambiando turno");
		Iterator<WaterlooListener> it=waterlooListeners.iterator();
		while(it.hasNext()){
			WaterlooListener w=it.next();
			if(agenteQueObtieneTurno.equals(this.jugador1)){
				System.out.println("rojo");
			}
			else
				System.out.println("azul");
			w.cambioTurno(agenteQueObtieneTurno);
		
		}
	}
	/**
	 * Metodo de la interfaz con el GUI que avisa cuando hubo un ganador en el juego
	 * @param agenteGanador Agente que ha ganado el juego
	 */
	@SuppressWarnings("unused")
	private void firefinJuego(Agente agenteGanador){
		Iterator<WaterlooListener> it=waterlooListeners.iterator();
		while(it.hasNext()){
			WaterlooListener w=it.next();
			w.finJuego(agenteGanador);
		
		}
	}
	/**
	 * Metodo de la interfaz con el GUI que avisa cuando se inicia el juego
	 * @param t tablero inicial con las fichas ubicadas 
	 */
	private void fireIniciarTableroGui(Tablero t){
		Iterator<WaterlooListener> it=waterlooListeners.iterator();
		while(it.hasNext()){
			WaterlooListener w=it.next();
			w.tableroGuiInicial(t,this.getJugador1(),this.getJugador2(),this.sistemaJuego);
			
		}
	}
	/**
	 * Metodo de la interfaz con el GUI que avisa se produce en el tablero un WATERLOO
	 * @param casillaGanadora Casilla que tiene la ficha que gano el waterloo
	 * @param casillaPerdedora Casilla que tiene la ficha que perdio el waterloo
	 * @param sistemaJuego Sistema de juego elegido en la configuracion del juego
	 * @param opcionBomba Opcion de quitar o no las bombas
	 */
	
	private void fireWaterloo(Casilla casillaGanadora,Casilla casillaPerdedora,int sistemaJuego,int opcionBomba){
		Iterator<WaterlooListener> it=waterlooListeners.iterator();
		while(it.hasNext()){
			WaterlooListener w=it.next();
			w.waterloo(casillaGanadora,casillaPerdedora,this.sistemaJuego,this.opcionBomba);
			
		}
	}
	/**
	 * Metodo de la interfaz con el GUI que avisa cuando en el tablero existe movimiento 
	 * de ficha
	 * @param mov Movimiento que se debe realizar
	 */
	
	private void fireMoverFicha(Movimiento mov){
		System.out.println("SandBox.fireMoverFicha:"+mov.getOrigen().getPosX()+", "+mov.getOrigen().getPosY()+" === "+mov.getDestino().getPosX()+" , "+mov.getDestino().getPosY());
		Iterator<WaterlooListener> it=waterlooListeners.iterator();
		while(it.hasNext()){
			WaterlooListener w=it.next();
			w.moverFicha(mov);
			esperarGui();
		}
	}
	
	/**
	 * Metodo que sirve para que el sandbox espere a que el GUI termine su animacion
	 */
	public synchronized void esperarGui(){
		waitingForGUI=true;
	}
	/**
	 * Metodo que sirve para avisar al sandbox que el GUI termino la animacion
	 */
	public synchronized void animationFinished(){
		waitingForGUI=false;
	}
	
	/**
	 * Metodo que valida el movimiento dentro de un tablero
	 * @param t Tablero donde se desea validar el movimiento
	 * @param mov Movimiento que se desea validar
	 * @param a Agente cuando el agente desea generar los tableros posibles
	 * @return True: Si el movimiento es valido
	 * False: si el movimiento que se desea validar es invalido
	 * @throws WaterlooLogicException Puede lanzar esta excepcion en algunos de estos casos:
	 * 1- La casilla de origen no contiene ficha
	 * 2- No existe ficha en la casilla de origen
	 * 3- El movimiento que desea realizar es invalido
	 * 4- La casilla de destino donde se desea mover la ficha es agua
	 *  
	 */
	public boolean validarMovimiento(Tablero t,Movimiento mov,Agente a) throws WaterlooLogicException{
		//Verifica si la ficha a mover existe
		if(mov.getOrigen().getFicha().getValor()==CASILLA_VACIA)
			throw new WaterlooLogicException("La ficha es nula",null);
		//Verifica si la ficha a mover es valida
		if(!this.validarFichaAMover(mov.origen.getFicha())){
			throw new WaterlooLogicException("La ficha "+mov.getOrigen().getFicha().getRango()+" no tiene movimiento",null);
		}
		if(!verificarMovimiento(mov,1))
				throw new WaterlooLogicException("La ficha "+mov.getOrigen().getFicha().getRango()+" no puede moverse mas de un paso o" +" la direccion es invalida ",null);

		//Verifica que la casilla donde se desea mover no sea agua
		
		if(casillaEsAgua(mov.destino))
			throw new WaterlooLogicException("La casilla donde se desea mover es agua",null );
		
		//Verifica que la casilla donde se desea mover la ficha esta vacia
		if(iscasillavacia(mov.destino)){
			moverFicha(t,mov,a);
			if(a!=null) {this.fireMoverFicha(mov);}
			return true;
		}
		else{
		
			if(mov.origen.getFicha().getColor()!=mov.getDestino().getFicha().getColor()){
				if(a!=null){
				this.fireMoverFicha(mov);
				}
				waterloo(t,mov,a);
				return true;
			}
			
		}
		return false;
	}
	/**
	 * Metodo que verifica si el movimiento que se desea realizar es valido
	 * @param mov Movimiento que se desea realizar
	 * @param pasos Pasos que se desea mover
	 * @return true: si el movimiento es valido
	 * false: si el movimiento es invalido
	 * @throws WaterlooLogicException Lanza esta excepcion si el movimiento esta fuera de
	 * los limites del tablero 
	 */
	public boolean verificarMovimiento(Movimiento mov,int pasos) throws WaterlooLogicException{
		if((mov.destino.getPosX()>9 || mov.getDestino().getPosY()>9)&&(mov.getDestino().getPosX()<0||mov.getDestino().getPosY()<0))
			throw new WaterlooLogicException("Las coordenadas estan fuera del tablero",null);
		if(mov.origen.getPosX()==mov.destino.getPosX() && mov.origen.getPosY()+pasos==mov.destino.getPosY())
			return true;
		else if(mov.origen.getPosX()+pasos==mov.destino.getPosX() && mov.origen.getPosY()==mov.destino.getPosY())
			return true;
		else if(mov.origen.getPosX()-pasos==mov.destino.getPosX() && mov.origen.getPosY()==mov.destino.getPosY())
			return true;
		else if(mov.origen.getPosX()==mov.destino.getPosX() && mov.origen.getPosY()-pasos==mov.destino.getPosY())
			return true;
		else
			return false;
	}
	/**
	 * Metodo que valida si la ficha que se desea mover es valida
	 * @param ficha Ficha que se desea mover
	 * @return true: si la ficha que se desea mover es valida
	 * false: si la ficha que se desea mover es invalida
	 * 
	 */
	public boolean validarFichaAMover(Ficha ficha){
		if(ficha.getValor()==BOMBA){
			return false;
		}
		if(ficha.getValor()==BANDERA){
			return false;
		}
		return true;
	}
	
	/**
	 * Metodo que verifica si la casilla es o no agua
	 * @param casillaVerificar Casilla que se desea verificar si es o no agua
	 * @return true: si la casilla es agua
	 * false: si la casilla no es agua
	 */
	public boolean casillaEsAgua(Casilla casillaVerificar){
		return casillaVerificar.isEsAgua();
	}
	/**
	 * Metodo que verifica si la casilla esta vacia o no
	 * @param destino Casilla que se desea verificar si esta o no vacia
	 * @return true: si la casilla verificada esta vacia
	 * false: si la casilla verificada no esta vacia
	 */
	private boolean iscasillavacia(Casilla destino) {
		if(destino.getFicha().getValor()==CASILLA_VACIA)
			return true;
		return false;
	}
	/**
	 * Metodo que mueve la ficha de acuerdo al movimiento que se desea realizar
	 * @param t Tablero donde se desea realizar el movimiento
	 * @param mov Movimiento que se desea realizar en el tablero
	 * @param a Agente si es que es el pedido del agente 
	 */
	private void moverFicha(Tablero t,Movimiento mov,Agente a){
		t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),mov.origen.getFicha());
		t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
		
		
	}
	
	/**
	 * Metodo que maneja el tema de waterloo dentro del tablero
	 * @param t Tablero donde se realiza el waterloo
	 * @param mov Movimiento que se desea realizar en el tablero 
	 * @param a Agente si es que el agente necesitara el waterloo para generar sus tableros  
	 */
	
	public void waterloo(Tablero t,Movimiento mov,Agente a){
		if(a!=null){
		}
		//Verifica si las fichas a realizar waterloo son cabo y bo0mba
		if(a==null){
			t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),mov.origen.getFicha());
			t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
			return;
		}
		if(mov.origen.getFicha().getValor()==CABO_DESARMADOR && mov.destino.getFicha().getValor()==BOMBA && mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
			//opcion bomba 1 es el que quita las bombas y la ficha que ataca
			if(this.opcionBomba==this.OPCION_QUITAR_BOMBA){
				t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),mov.origen.getFicha());
				t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
				fireWaterloo(mov.origen,mov.destino,this.sistemaJuego,this.OPCION_QUITAR_BOMBA);
				return;
			}
			//opcion bomba 2 no quita la bomba 
			else{
				t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),mov.origen.getFicha());
				t.setFichaEnTablero(mov.getOrigen().getPosX(),mov.getOrigen().getPosY(), new Ficha());
				fireWaterloo(mov.origen,mov.destino,this.sistemaJuego,this.OPCION_NO_QUITAR_BOMBA);				
				return;
			}
		}
		
		//Verifica que la pieza atacada es bomba entonces evalua las opciones
		else if(mov.getDestino().getFicha().getValor()==BOMBA && mov.getOrigen().getFicha().getColor()!=mov.getDestino().getFicha().getColor()){
			//Opcion bomba 1 entonces mueren ambas fichas
			if(opcionBomba==OPCION_QUITAR_BOMBA){
				t.setFichaEnTablero(mov.getOrigen().getPosX(),mov.getOrigen().getPosY(),new Ficha());
				t.setFichaEnTablero(mov.getDestino().getPosX(),mov.getDestino().getPosY(),new Ficha());
				fireWaterloo(mov.origen,mov.destino,this.sistemaJuego,this.OPCION_QUITAR_BOMBA);
			}
		
			//Opcion bomba 2 entonces solo muere la ficha que ataca	
			else
				t.setFichaEnTablero(mov.getOrigen().getPosX(),mov.getOrigen().getPosY(),new Ficha());
				fireWaterloo(mov.destino,mov.origen, this.sistemaJuego,this.OPCION_NO_QUITAR_BOMBA);
		}
		//Si ataca el mariscal muere el espia
		else if(mov.origen.getFicha().getValor()==MARISCAL && mov.destino.getFicha().getValor()==ESPIA && mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
			t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),mov.origen.getFicha());
			t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
			fireWaterloo(mov.origen,mov.destino, this.sistemaJuego,0);
			return;
		}
		//Si ataca el espia muere el mariscal
		else if(mov.origen.getFicha().getValor()==ESPIA && mov.destino.getFicha().getValor()==MARISCAL && mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
			t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),mov.origen.getFicha());
			t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
			fireWaterloo(mov.origen,mov.destino, this.sistemaJuego,0);
			return;
		}
		//Ha capturado a la bandera contraria
		else if(mov.destino.getFicha().getRango().equalsIgnoreCase("bandera")&& mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
			t.setFichaEnTablero(mov.getDestino().getPosX(),mov.getDestino().getPosY(),mov.getOrigen().getFicha());
			t.setFichaEnTablero(mov.getOrigen().getPosX(),mov.getOrigen().getPosY(), new Ficha());
			firefinJuego(a);
			System.out.println("de"+mov.origen.getPosX()+mov.origen.getPosY()+" a"+mov.destino.getPosX()+mov.destino.getPosY());
			
			this.finJuego=true;
		}
		else{
			if(mov.origen.getFicha().getValor() > mov.destino.getFicha().getValor()&&mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
				t.setFichaEnTablero(mov.destino.getPosX(), mov.destino.getPosY(),mov.origen.getFicha());
				t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
				fireWaterloo(mov.origen,mov.destino, this.sistemaJuego,0);
			}
			else if(mov.origen.getFicha().getValor() < mov.destino.getFicha().getValor()&&mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
				t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
				fireWaterloo(mov.destino,mov.origen, this.sistemaJuego,0);
			}
			else{
				//Mueren las dos fichas si son del mismo valor
				if(this.sistemaJuego==SISTEMA_EUROPEO && mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
					t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
					t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),new Ficha());
					fireWaterloo(mov.origen,mov.destino, this.sistemaJuego,this.OPCION_QUITAR_BOMBA);
				}
				else{
					//Muere la pieza atacada
					if(mov.origen.getFicha().getColor()!=mov.destino.getFicha().getColor()){
						t.setFichaEnTablero(mov.destino.getPosX(),mov.destino.getPosY(),mov.origen.getFicha());
						t.setFichaEnTablero(mov.origen.getPosX(),mov.origen.getPosY(),new Ficha());
						fireWaterloo(mov.origen,mov.destino, this.sistemaJuego,0);
					}
				}
			}
		}
	}
	
	
	@Override
	public Tablero getTablero(Tablero t, Movimiento m)throws WaterlooLogicException {
		Tablero tabaux=new Tablero();
		if(m.origen.getFicha().getValor()==CASILLA_VACIA)
			throw new WaterlooLogicException("No existe ficha",null);
		if(!validarMovimiento(tabaux, m,null )){
			throw new WaterlooLogicException("El tablero no es valido",null);
		}
		return tabaux;
	}
	
	@Override
	public int getValor(Ficha f, Agente a) throws WaterlooLogicException,WaterlooApplicationException {
		if(a.getColor()!=f.getColor()){
			throw new WaterlooLogicException("no se haga del oso",null);
		}
		return f.getValor();
	}
	
	@Override
	public boolean hayFicha(int i, int j) {
		if(this.tableroJuego.getCasillaDelTablero(i, j).getFicha().getValor()!=CASILLA_VACIA)
			return true;
		return false;
	}
	
	/**
	 * Metodo que copia un tablero en otro tablero
	 * @param tabviejo Tablero que se desea copiar
	 * @param tabnuevo Tablero donde se copia el tablero que se desea copiar
	 * @return Tablero que se copio
	 */
	public Tablero copiarTablero(Tablero tabviejo,Tablero tabnuevo){
		for (int i = 0; i < 10; i++){
			for(int j=0;j<10;j++){
				tabnuevo.setCasillaEnTablero(i, j, tabviejo.getCasillaDelTablero(i, j));
			}
		}
		return tabnuevo;
	}

	
	@Override
	public Ficha getFicha(Agente a, int i, int j){
		if(this.tableroJuego.getCasillaDelTablero(i, j).getFicha().getColor()==a.getColor()){
			return this.tableroJuego.getCasillaDelTablero(i, j).getFicha();
		}
		return null;
	}	
}